import os
import pandas as pd
from py2neo import Graph
from py2neo import Graph, Node, Relationship, NodeMatcher
import json

def node_to_dict_noId(node: Node):
    return {
        # "identity": node.identity,
        "labels": list(node.labels),
        "properties": dict(node)
    }

def node_to_dict_with_Id(node: Node):
    return {
        "identity": node.identity,
        "labels": list(node.labels),
        "properties": dict(node)
    }




from pathlib import Path
from typing import Dict, Any

def debug_save_standard_process_steps(
    standard_process_steps: Dict[str, Any],
    base_dir: str = "debug_process_steps"
):
    """
    调试函数：将标准工序工步字典按层级结构保存为文件系统
    
    参数:
        standard_process_steps (dict): 来自 SmartProcessDesigner.new_graph['标准工序工步']
        base_dir (str): 保存的根目录路径
    
    目录结构示例:
    debug_process_steps/
    └── process_1_初成形/
        ├── 工序节点.json
        ├── 工序检列表.json
        └── step_1_粗铣/
            ├── 工步节点.json
            └── 工步检列表.json
    """
    base_path = Path(base_dir)
    base_path.mkdir(parents=True, exist_ok=True)

    if not standard_process_steps:
        print("⚠️ 无标准工序工步数据可保存")
        return

    # 按工序号排序（支持数字或字符串）
    try:
        sorted_process_ids = sorted(standard_process_steps.keys(), key=lambda x: int(x) if x.isdigit() else x)
    except:
        sorted_process_ids = list(standard_process_steps.keys())

    for proc_id in sorted_process_ids:
        proc_info = standard_process_steps[proc_id]
        
        # 获取工序名称
        proc_name = "未知工序"
        if proc_info.get("工序节点") and isinstance(proc_info["工序节点"], dict):
            proc_name = proc_info["工序节点"]["properties"].get("工序名称", f"工序{proc_id}")
        
        # 清理文件名（只保留字母、数字、下划线、连字符、空格）
        safe_proc_name = "".join(c for c in proc_name if c.isalnum() or c in ("_", "-", " "))
        proc_folder = base_path / f"process_{proc_id}_{safe_proc_name}"
        proc_folder.mkdir(exist_ok=True)

        # 保存工序节点
        with open(proc_folder / "工序节点.json", "w", encoding="utf-8") as f:
            json.dump(proc_info.get("工序节点", {}), f, ensure_ascii=False, indent=2)

        # 保存工序检列表
        with open(proc_folder / "工序检列表.json", "w", encoding="utf-8") as f:
            json.dump(proc_info.get("工序检列表", []), f, ensure_ascii=False, indent=2)

        # 处理工步
        steps = proc_info.get("工步列表", [])
        for idx, step_info in enumerate(steps, 1):
            step_node = step_info.get("工步节点", {})
            step_name = "未知工步"
            if isinstance(step_node, dict):
                step_name = step_node["properties"].get("工步名称", f"工步{idx}")
            
            safe_step_name = "".join(c for c in step_name if c.isalnum() or c in ("_", "-", " "))
            step_folder = proc_folder / f"step_{idx}_{safe_step_name}"
            step_folder.mkdir(exist_ok=True)

            with open(step_folder / "工步节点.json", "w", encoding="utf-8") as f:
                json.dump(step_node, f, ensure_ascii=False, indent=2)

            with open(step_folder / "工步检列表.json", "w", encoding="utf-8") as f:
                json.dump(step_info.get("工步检列表", []), f, ensure_ascii=False, indent=2)

        print(f"✅ 已保存工序 {proc_id}: {proc_name}（含 {len(steps)} 个工步）")

    print(f"🎉 所有工序工步调试文件已保存至: {base_path.resolve()}")


class SmartProcessDesigner:
    def __init__(self, neo4j_url, neo4j_auth, log_file_path=None):
        self.graph = None
        self._log_file_path = log_file_path
        try:
            self.graph = Graph(neo4j_url, auth=neo4j_auth)
            self.graph.run("RETURN 1")  
            print("✅ Neo4j 连接成功")
        except Exception as e:
            print(f"❌ Neo4j 连接失败: {e}")
            raise ConnectionError(f"neo4j数据库连接失败: {e}") from e

        # 查询到的相似的历史图
        self.similar_history_graph = {
            "图纸节点": None,
            "元件节点": None,
            "元件类型节点": None,
            "终检节点": [],
            "标准工序工步": {},
        }

        # 新图纸在neo4j中新的子图,与create函数对应
        self.new_graph = {
            "图纸节点": {},
            "元件节点": {},
            "元件类型节点": {},
            "终检节点": [],
            "标准工序工步": {}  # 新增：用于保存复用的标准工序工步信息
        }
        
        # 初始化终检清单存储
        self.final_inspection_list = []
        self._product_name = None  # 新增：用于存储产品名称


    def get_drawing_final_inspection(self, drawing_id):
        """查询指定图纸ID的终检信息并返回处理后的列表"""

        # 执行查询
        cypher_query = """
        MATCH (d:图纸 {图纸ID: $drawing_id})-[:有终检项]->(fi:终检)
        RETURN fi AS 终检
        """
        result = self.graph.run(cypher_query, drawing_id=drawing_id)
        
        # 处理结果并返回列表
        return [node_to_dict_with_Id(record["终检"]) for record in result]


    def read_final_inspection(self, df):
        """子函数：读取终检清单并存储（私有方法）"""
        try:
            # 列名改为英文（用于处理）
            required_cols = ["check_class", "name", "check_face", "technical_index"]
            for col in required_cols:
                if col not in df.columns:
                    raise ValueError(f"pandas数据 缺少必要列: {col}")

            # 所有列空值填充为字符串空值，保留原始数据
            for col in df.columns:
                df[col] = df[col].fillna("").astype(str)

            # 转换为 list[dict] 并存储
            self.final_inspection_list = df.to_dict("records")

            # 最后将列名转换为中文，保持与后续代码兼容
            chinese_column_mapping = {
                "check_class": "原始分类",
                "name": "检验项目名称",
                "check_face": "检测面",
                "technical_index": "设计值技术指标"
            }

            # 转换列名为中文
            for record in self.final_inspection_list:
                for eng_col, chn_col in chinese_column_mapping.items():
                    if eng_col in record:
                        record[chn_col] = record[eng_col]
                        # 可以选择是否删除英文列名
                        # del record[eng_col]

            return True
        except Exception as e:
            print(f"❌ 终检清单加载失败: {str(e)}")
            return False






    def read_product_name(self, product_name):
        """读取产品名称"""
        self._product_name = product_name

    def do1_通过产品名称找元件(self):
        """根据产品名称（BM25模糊匹配）查找对应的元件、元件类型和图纸"""
        try:
            # 1. 前置校验：确保Neo4j已连接
            if not self.graph:
                raise ConnectionError("Neo4j未连接，无法执行查询")

            # 2. 从图谱中获取所有元件的产品名称（构建BM25语料库）
            corpus_query = "MATCH (p:元件) RETURN p.产品名称 AS product_name"
            corpus_records = self.graph.run(corpus_query).data()
            if not corpus_records:
                raise ValueError("图谱中未查询到任何元件数据")
            
            # 提取产品名称列表作为语料库
            corpus = [record["product_name"] for record in corpus_records]

            # 3. 用BM25算法匹配输入的产品名称
            matched_name = self._bm25_match(self._product_name, corpus)
            if not matched_name:
                raise ValueError(f"未找到与'{self._product_name}'匹配的元件")

            # 4. 根据BM25匹配结果查询完整信息（精确匹配）
            cypher_query = """
            MATCH (p:元件)-[:属于类别]->(pt:元件类型)
            WHERE p.产品名称 = $matched_name
            OPTIONAL MATCH (p)-[:有图纸]->(d:图纸)
            RETURN p AS 元件节点, pt AS 元件类型节点, d AS 图纸节点
            LIMIT 1
            """
            
            records = self.graph.run(cypher_query, matched_name=matched_name).data()
            if not records:
                raise ValueError(f"未查询到产品名称为'{matched_name}'的元件详情")

            # 5. 保存查询结果
            result = records[0]
            self.similar_history_graph["元件节点"] = result["元件节点"]
            self.similar_history_graph["元件类型节点"] = result["元件类型节点"]
            self.similar_history_graph["图纸节点"] = result["图纸节点"]

            print(f"✅ 步骤1完成：元件、元件类型、图纸节点已保存")

        except Exception as e:
            print(f"查询失败：{str(e)}")
            raise Exception(f"查询失败：{str(e)}") from e


    def _bm25_match(self, input_str, corpus):
        """内部方法：使用BM25算法匹配最相似的产品名称"""
        try:
            from rank_bm25 import BM25Okapi
            import jieba
        except ImportError:
            raise ImportError("请先安装依赖：pip install rank_bm25 jieba")

        # 中文分词（处理空格、特殊字符）
        def tokenize(text):
            # 预处理：移除特殊字符，统一空格
            text = text.replace("(", "").replace(")", "").replace(" ", "")
            return list(jieba.cut(text))  # 分词

        # 语料库分词
        tokenized_corpus = [tokenize(name) for name in corpus]
        # 输入值分词
        tokenized_input = tokenize(input_str)

        # 构建BM25模型并计算分数
        bm25 = BM25Okapi(tokenized_corpus)
        scores = bm25.get_scores(tokenized_input)

        # 取分数最高的结果（过滤无效分数）
        max_score_idx = scores.argmax()
        if scores[max_score_idx] < 0.1:  # 阈值可根据实际情况调整
            return None
        return corpus[max_score_idx]




    

    def do2_获取历史终检节点(self):
        """步骤2：根据找到的图纸节点，通过'有终检项'关系获取对应的终检节点并保存"""
        try:
            # 前置校验：确保已找到相似的图纸节点
            if not self.similar_history_graph or "图纸节点" not in self.similar_history_graph:
                raise ValueError("请先执行步骤1找到相似的图纸节点")
            if not self.graph:
                raise ConnectionError("Neo4j未连接，无法执行查询")

            # 获取图纸节点的图纸ID
            drawing_node = self.similar_history_graph["图纸节点"]
            drawing_id = drawing_node.get("图纸ID")
            if not drawing_id:
                raise ValueError("图纸节点缺少必要的图纸ID属性")

            # 构建Cypher查询，通过'有终检项'关系查找终检节点
            cypher_query = """
            MATCH (d:图纸 {图纸ID: $drawing_id})-[:有终检项]->(fi:终检)
            RETURN fi AS 终检节点
            """

            # 执行查询
            records = self.graph.run(cypher_query, drawing_id=drawing_id).data()
            if not records:
                print("⚠️ 未查询到与该图纸关联的终检节点")
                return True

            # 提取并保存终检节点信息
            inspection_nodes = [record["终检节点"] for record in records]
            self.similar_history_graph["终检节点"] = inspection_nodes
            
            print(f"✅ 步骤2完成：成功获取 {len(inspection_nodes)} 个终检节点并保存到similar_history_graph")
            return True

        except Exception as e:
            print(f"❌ 步骤2失败：{str(e)}")
            return False


    def do3_获取工序工步以及对应检测项(self):
        """步骤3：根据元件类型节点，获取工序、工步及所有检项（使用 collect 聚合）"""
        try:
            if not self.similar_history_graph or "元件类型节点" not in self.similar_history_graph:
                raise ValueError("请先执行步骤1找到相似的元件类型节点")
            if not self.graph:
                raise ConnectionError("Neo4j未连接")

            component_type_node = self.similar_history_graph["元件类型节点"]
            process_id = component_type_node.get("工艺ID")
            if not process_id:
                raise ValueError("元件类型节点缺少工艺ID")

            # ✅ 使用 collect 聚合，确保每个工步的所有检项在一个列表中
            cypher_query = """
            MATCH (pt:元件类型 {工艺ID: $process_id})-[:分类包含工序]->(op:工序)
            OPTIONAL MATCH (op)-[:有工序检项]->(oi:工序检)
            WITH op, collect(oi) AS oi_list
            OPTIONAL MATCH (op)-[:有工步]->(step:工步)
            OPTIONAL MATCH (step)-[:有工步检项]->(si:工步检)
            WITH op, oi_list, step, collect(si) AS si_list
            RETURN 
                op AS 工序节点,
                oi_list AS 工序检列表,
                step AS 工步节点,
                si_list AS 工步检列表
            ORDER BY op.工序号, step.工步号
            """

            records = self.graph.run(cypher_query, process_id=process_id).data()
            if not records:
                print("⚠️ 未查询到工序工步数据")
                return True

            from collections import defaultdict
            process_steps_map = defaultdict(lambda: {
                "工序节点": None,
                "工序检列表": [],
                "工步列表": []
            })

            for record in records:
                op_node = record["工序节点"]
                proc_id = str(op_node.get("工序号", "unknown"))

                # 初始化工序
                if process_steps_map[proc_id]["工序节点"] is None:
                    process_steps_map[proc_id]["工序节点"] = op_node
                    # 过滤 None 并去重（collect 可能包含 null）
                    oi_list = [n for n in record["工序检列表"] if n is not None]
                    process_steps_map[proc_id]["工序检列表"] = oi_list

                # 处理工步
                step_node = record["工步节点"]
                if step_node is not None:
                    si_list = [n for n in record["工步检列表"] if n is not None]
                    process_steps_map[proc_id]["工步列表"].append({
                        "工步节点": step_node,
                        "工步检列表": si_list
                    })

            self.similar_history_graph["标准工序工步"] = dict(process_steps_map)
            print(f"✅ 步骤3完成：获取 {len(process_steps_map)} 个工序，工步检项已完整聚合")
            return True

        except Exception as e:
            print(f"❌ 步骤3失败：{str(e)}")
            import traceback
            traceback.print_exc()
            return False


    def create1_新元件信息(self):
        """创建步骤1：基于历史相似元件创建新的元件信息"""
        try:
            # 前置校验：确保已找到相似的节点
            required_keys = ["图纸节点", "元件节点", "元件类型节点"]
            for key in required_keys:
                if not self.similar_history_graph or key not in self.similar_history_graph:
                    raise ValueError(f"请先执行步骤1找到相似的{key}")
            
            # 从历史节点复制信息
            node_similar_drawing = self.similar_history_graph["图纸节点"]
            node_similar_component = self.similar_history_graph["元件节点"]
            node_similar_component_type = self.similar_history_graph["元件类型节点"]
            
            
            # 创建新的节点信息（复制历史信息）
            node_dict = node_to_dict_noId(node_similar_drawing)
            node_new_drawing = Node(**node_dict)
            node_dict = node_to_dict_noId(node_similar_component)
            node_new_component = Node(**node_dict)
            node_dict = node_to_dict_noId(node_similar_component_type)
            node_new_component_type = Node(**node_dict)

            # 保存到new_graph 是字典
            self.new_graph["图纸节点"] = node_new_drawing
            self.new_graph["元件节点"] = node_new_component
            self.new_graph["元件类型节点"] = node_new_component_type
 
            print("✅ 创建create1_新元件信息完成：成功创建新的图纸、元件和元件类型信息")
            return True
            
        except Exception as e:
            print(f"❌ 创建create1_新元件信息失败：{str(e)}")
            return False

    def create2_新终检信息(self):
        """创建create2_新终检信息：基于BM25算法匹配新终检清单与历史终检项"""
        try:
            # 前置校验：确保终检清单已加载
            if not self.final_inspection_list:
                raise ValueError("请先加载有效的终检清单")
            
            # 获取历史图纸ID
            drawing_node = self.similar_history_graph["图纸节点"]
            drawing_id = drawing_node["图纸ID"]
            
            hist_inspection_list = self.get_drawing_final_inspection(drawing_id)
            
            if not hist_inspection_list:
                raise ValueError("未找到历史终检项，无法进行BM25匹配")
            
            # 将新终检清单转换为DataFrame格式用于BM25匹配
            import pandas as pd
            new_df = pd.DataFrame(self.final_inspection_list)
            
            # 使用BM25算法进行匹配
            from utils.fun3 import bm25_match_inspections
            matched_inspections = bm25_match_inspections(new_df, hist_inspection_list)
            
            # 将匹配结果转换为新终检节点格式
            new_inspection_nodes = []
            for item in matched_inspections:
                # 查找对应的历史终检节点
                hist_node = None
                for hist_item in hist_inspection_list:
                    if hist_item.get("identity") == item["identity"]:
                        hist_node = hist_item
                        break
                
                if hist_node:
                    # 保留图谱内置ID，用新属性覆盖其余属性
                    new_node = hist_node.copy()  # 直接使用处理后的历史节点
                    new_node["properties"] = item["properties"]  # 使用BM25匹配后的新属性
                    new_inspection_nodes.append(new_node)
            
            self.new_graph["终检节点"] = new_inspection_nodes
            
            print(f"✅ 创建create2_新终检信息完成：通过BM25算法成功匹配 {len(new_inspection_nodes)} 个终检项")
            return True
            
        except Exception as e:
            print(f"❌ 创建create2_新终检信息失败：{str(e)}")
            return False

    def create3_复用标准工序工步(self):
        """
        创建create3_复用标准工序工步：复用历史相似元件的标准工序工步信息到new_graph中
        处理嵌套的Neo4j节点结构：工序->工步->检项
        """
        print("\n📋 create3：复用标准工序工步信息...")
        
        # 检查是否有历史标准工序工步数据
        if not self.similar_history_graph.get("标准工序工步"):
            print("⚠️  没有找到历史标准工序工步数据，跳过复用")
            return
            
        # 初始化new_graph中的标准工序工步结构
        if "标准工序工步" not in self.new_graph:
            self.new_graph["标准工序工步"] = {}
        
        # 复用历史标准工序工步数据
        history_steps = self.similar_history_graph["标准工序工步"]
        
        # 遍历每个工序（顶级键是工序号）
        for process_id, process_info in history_steps.items():
            # 创建新的工序信息结构
            new_process_info = {
                "工序节点": None,
                "工序检列表": [],
                "工步列表": []
            }
            
            # 处理工序节点（Neo4j Node对象）
            if process_info.get("工序节点"):
                if isinstance(process_info["工序节点"], Node):
                    new_process_info["工序节点"] = node_to_dict_with_Id(process_info["工序节点"])
                else:
                    new_process_info["工序节点"] = process_info["工序节点"]
            
            # 处理工序检列表（列表中的Neo4j Node对象）
            for inspection_node in process_info.get("工序检列表", []):
                if isinstance(inspection_node, Node):
                    new_process_info["工序检列表"].append(node_to_dict_with_Id(inspection_node))
                else:
                    new_process_info["工序检列表"].append(inspection_node)
            
            # 处理工步列表（嵌套结构）
            for step_info in process_info.get("工步列表", []):
                new_step_info = {
                    "工步节点": None,
                    "工步检列表": []
                }
                
                # 处理工步节点
                if step_info.get("工步节点"):
                    if isinstance(step_info["工步节点"], Node):
                        new_step_info["工步节点"] = node_to_dict_with_Id(step_info["工步节点"])
                    else:
                        new_step_info["工步节点"] = step_info["工步节点"]
                
                # 处理工步检列表
                for step_inspection in step_info.get("工步检列表", []):
                    if isinstance(step_inspection, Node):
                        new_step_info["工步检列表"].append(node_to_dict_with_Id(step_inspection))
                    else:
                        new_step_info["工步检列表"].append(step_inspection)
                
                new_process_info["工步列表"].append(new_step_info)
            
            # 保存到new_graph
            self.new_graph["标准工序工步"][process_id] = new_process_info
        
        print(f"✅ 创建create3_复用标准工序工步完成：成功复用 {len(self.new_graph['标准工序工步'])} 个工序的标准工序工步信息")
        total_steps = sum(len(process_info.get("工步列表", [])) for process_info in self.new_graph["标准工序工步"].values())
        print(f"   共包含 {total_steps} 个工步信息")


   


    def create4_终检分解(self):
        """步骤3：终检指标分解（终检→规则→工序检）"""
        try:
            # --------------------------
            # 1. 前置校验
            # --------------------------
            if not self.new_graph or "终检节点" not in self.new_graph:
                raise ValueError("请先创建新终检节点信息")
            if not self.new_graph.get("标准工序工步"):
                raise ValueError("请先创建标准工序工步信息")

            print("\n--- 开始终检指标分解 ---")
            print(f"处理新图纸的终检节点，共 {len(self.new_graph['终检节点'])} 个终检项")

            # --------------------------
            # 2. 导入计算模块
            # --------------------------
            from rule.设计值解析器 import DesignValueParser
            from rule.规则计算引擎 import SafeMathEvaluator, RuleCalculator  

            # 初始化计算器
            calculator = RuleCalculator()

            # Rule1/Rule2 到规则类型的映射
            rule_name_to_type = {
                "rule_1": "双一致",
                "rule_2": "数值收敛"
            }

            # 记录更新成功的数量
            update_count = 0

            # --------------------------
            # 3. For循环处理每个新终检节点
            # --------------------------
            print(f"开始处理 {len(self.new_graph['终检节点'])} 个新终检节点...")
            
            for i, new_final_node in enumerate(self.new_graph["终检节点"]):
                # 获取新终检节点的信息
                new_final_properties = new_final_node.get("properties", {})
                neo4j_id_new_final = new_final_node.get("identity")  # 内置ID
                
                identifier = new_final_properties.get("main_标识ID号", "无标识号")
                new_final_value = new_final_properties.get("设计值技术指标")
                new_final_name = new_final_properties.get("检验项目名称")
                
                # 不再跳过缺少标识号的节点，因为BM25匹配可能没有main_标识ID号
                # 但仍然需要内置ID来进行历史规则链路查询
                if not neo4j_id_new_final:
                    print(f"  ⚠️ 终检节点 {i} 缺少内置ID，跳过")
                    continue
                
                print(f"\n[{i+1}] 处理终检节点: {identifier} - {new_final_name} (内置ID: {neo4j_id_new_final}, 设计值: {new_final_value})")
                
                # --------------------------
                # 4. 通过内置ID查询对应的历史规则链路 - 返回节点对象
                # --------------------------
                hist_query = """
                MATCH (hist_fi:终检)-[:rule_终检分解]->(rule:规则)-[:rule_生成工序检]->(hist_pi:工序检)<-[:有工序检项]-(hist_proc:工序)
                WHERE id(hist_fi) = $终检节点ID
                RETURN
                    hist_fi AS 终检节点,
                    hist_proc AS 工序节点,
                    hist_pi AS 工序检节点,
                    rule AS 规则节点
                """
                
                hist_records = self.graph.run(hist_query, 终检节点ID=neo4j_id_new_final).data()
                
                if not hist_records:
                    print(f"  ⚠️ 未找到内置ID为 {neo4j_id_new_final} 的历史规则链路，跳过")
                    continue
                
                print(f"  🔗 找到 {len(hist_records)} 条相关的历史工序检记录")
                
                # --------------------------
                # 5. 遍历处理每条历史工序检记录
                # --------------------------
                for j, hist_record in enumerate(hist_records):
                    # 从节点对象中提取所需属性和内置ID
                    rule_node = hist_record["规则节点"]
                    hist_proc_node = hist_record["工序节点"]
                    hist_pi_node = hist_record["工序检节点"]

                    rule_name = rule_node.get("rule_name")
                    rule_calculator = rule_node.get("rule_calculator")
                    
                    neo4j_id_process = hist_proc_node.identity  # 工序节点neo4j内置ID
                    process_id = hist_proc_node.get("工序号")
                    process_name = hist_proc_node.get("工序名称")
                    
                    # 获取工序检节点的基本信息用于打印
                    neo4j_id_process_inspection = hist_pi_node.identity  # 工序检节点neo4j内置ID
                    process_inspection_type = hist_pi_node.get("分类名称")
                    process_inspection_name = hist_pi_node.get("检验项目名称")
                    process_inspection_value = hist_pi_node.get("设计值技术指标")
                    
                    print(f"    [{j+1}]  工序{process_id}-{process_name} || 检测项 [{process_inspection_type}:{process_inspection_name}] ||  (规则: {rule_name})")
                    
                    # 根据规则类型计算新工序检值
                    new_process_value = None
                    
                    try:
                        # 情况1: rule_9999 直接复用历史工序检设计值
                        if rule_name == "rule_9999":
                            if process_inspection_value:
                                new_process_value = process_inspection_value
                                print(f"      🔄 rule_9999: 直接复用历史值 {new_process_value}")
                            else:
                                print(f"      ⚠️ rule_9999 但没有历史设计值")
                                
                        # 情况2: 正常规则（rule_1 / rule_2）
                        elif rule_name in rule_name_to_type:
                            rule_type = rule_name_to_type[rule_name]
                            
                            # 调用 RuleCalculator 计算
                            new_process_value = calculator.calculate_process_value(
                                new_final_value=new_final_value,
                                rule_type=rule_type,
                                rule_formula=rule_calculator
                            )
                            
                            print(f"      🧮 {rule_type}: 使用公式 '{rule_calculator}' 计算得到 {new_process_value}")
                            
                        else:
                            print(f"      ⚠️ 未知规则类型: {rule_name}")
                            
                    except Exception as e:
                        print(f"      ❌ 计算失败: {str(e)}")
                        
                    # 更新到self.new_graph的标准工序工步中，传入neo4j内置ID和整个工序检节点
                    if new_process_value is not None:
                        # 更新new_graph中的标准工序工步，传入整个工序检节点
                        self._update_standard_process_with_inspection(
                            process_id=process_id,
                            new_inspection_value=new_process_value,
                            process_inspection_node=hist_pi_node,  # 传入整个工序检节点
                            neo4j_id_process=neo4j_id_process  # 工序节点neo4j内置ID
                        )
                        
                        update_count += 1
                        print(f"      ✅ 已更新标准工序工步: {new_process_value} (工序ID: {neo4j_id_process}, 工序检ID: {neo4j_id_process_inspection})")
                    else:
                        print(f"      ❌ 计算失败或无结果，未更新")

            print(f"\n✅ 终检分解完成，共更新 {update_count} 项标准工序工步数据")
            return True

        except Exception as e:
            print(f"❌ 步骤3失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return False
    
    

    def _update_standard_process_with_inspection(self, process_id, new_inspection_value, process_inspection_node, 
                                                 neo4j_id_process=None):
        """辅助方法：将计算得到的工序检值更新到self.new_graph的标准工序工步中的指定工序检"""
        if "标准工序工步" not in self.new_graph:
            return
            
        # 从工序检节点中提取所需信息
        process_inspection_name = process_inspection_node.get("检验项目名称")
        # process_inspection_type = process_inspection_node.get("分类名称")
        # process_inspection_surface = process_inspection_node.get("检测面")
        # process_inspection_unit = process_inspection_node.get("计量单位")
        neo4j_id_process_inspection = process_inspection_node.identity  # 工序检节点neo4j内置ID
        # # 再加上 检测角度 注释 检测光波长 检测口径通光口径 说明
        # process_inspection_angle = process_inspection_node.get("检测角度", "")
        # process_inspection_remark = process_inspection_node.get("注释", "")
        # process_inspection_wavelength = process_inspection_node.get("检测光波长", "")
        # process_inspection_aperture = process_inspection_node.get("检测口径通光口径", "")
        # process_inspection_desc = process_inspection_node.get("说明", "")
        
        # 注意：这里的标准工序工步是字典格式，key是工序号
        if str(process_id) in self.new_graph["标准工序工步"]:
            process_info = self.new_graph["标准工序工步"][str(process_id)]
            
            # 构建新的检测项目数据，包含节点的所有相关属性
            # inspection_data = {
            #     "检验项目名称": process_inspection_name,
            #     "分类名称": process_inspection_type,
            #     "检测面": process_inspection_surface,
            #     "设计值技术指标": new_inspection_value,
            #     "计量单位": process_inspection_unit,
            #     # 再加上 检测角度 注释 检测光波长 检测口径通光口径 说明
            #     "检测角度": process_inspection_angle,
            #     "注释": process_inspection_remark,
            #     "检测光波长": process_inspection_wavelength,
            #     "检测口径通光口径": process_inspection_aperture,
            #     "说明": process_inspection_desc,
            # }
                
            # 查找并更新指定的工序检，使用neo4j内置ID进行精确匹配
            found = False
            if "工序检列表" in process_info:
                for i, existing_inspection in enumerate(process_info["工序检列表"]):
                    # 使用identity字段进行精确匹配
                    if existing_inspection.get("identity") == neo4j_id_process_inspection:
                        # 更新找到的工序检
                        process_info["工序检列表"][i]['properties']['设计值技术指标'] = new_inspection_value
                        found = True
                        print(f"      ✅ 更新工序{process_id}的工序检: {process_inspection_name} = {new_inspection_value} (identity: {neo4j_id_process_inspection})")
                        break
                
                # 如果没有找到匹配的工序检
                if not found:
                    print(f'警告，找不到{neo4j_id_process_inspection}对应的工序检, 有大问题')
            else:
                print('警告：工序检列表不存在!!! ')

    # ==========================
    # 新增：do5  工序检 → 工步检
    # ==========================
    def create5_工序检再分解(self):
        """步骤5：把已经分解到工序检的指标，再按历史规则二次分解到工步检"""
        try:
            if not self.new_graph.get("标准工序工步"):
                raise ValueError("缺少标准工序工步数据，请先执行终检分解或复用工序工步")
            from rule.规则计算引擎 import RuleCalculator
            calculator = RuleCalculator()

            print("\n--- 开始工序检→工步检二次分解 ---")
            total_updated = 0

            # 给每个工序检编号（跨工序连续计数）
            counter = 0

            for process_id, process_info in self.new_graph["标准工序工步"].items():
                process_node = process_info.get("工序节点")
                if not process_node:
                    continue
                for proc_insp in process_info.get("工序检列表", []):
                    counter += 1
                    proc_insp_name   = proc_insp["properties"].get("检验项目名称")
                    proc_insp_value  = proc_insp["properties"].get("设计值技术指标")
                    proc_insp_id     = proc_insp.get("identity")
                    if proc_insp_value is None:
                        continue

                    # 打印「源头」
                    print(f"\n[{counter}] 处理工序检: {proc_insp_name} "
                        f"(内置ID: {proc_insp_id}, 设计值: {proc_insp_value})")

                    # 查询历史规则链路
                    rule_query = """
                    MATCH (pi:工序检)-[:rule_工序检分解]->(rule:规则)-[:rule_生成工步检]->(si:工步检)<-[:有工步检项]-(step:工步)
                    WHERE id(pi) = $pi_id
                    RETURN pi, rule, si, step
                    """
                    hist_rules = self.graph.run(rule_query, pi_id=proc_insp_id).data()
                    if not hist_rules:
                        print(f"  ⚠️ 无历史分解规则，跳过")
                        continue

                    print(f"  🔗 找到 {len(hist_rules)} 条相关的历史工步检记录")
                    # 逐条处理
                    for idx, row in enumerate(hist_rules, 1):
                        rule_node = row["rule"]
                        step_node = row["step"]
                        si_node   = row["si"]
                        # pi_node   = row["pi"]

                        rule_name  = rule_node.get("rule_name")
                        rule_formula = rule_node.get("rule_calculator")
                        step_id    = step_node.get("工步号")
                        step_name  = step_node.get("工步名称", f"工步{step_id}")
                        si_name    = si_node.get("记录项检验项名称")
                        # si_surface = si_node.get("检测面")
                        si_category= si_node.get("分类名称")
                        si_identity= si_node.identity
                        if si_identity == 4152:
                            print() # 调试用的
                        # 计算新值
                        new_si_value = None
                        try:
                            if rule_name == "rule_9999":
                                new_si_value = si_node.get("设计值技术指标")
                                print(f"    [{idx}]  工序信息：{process_id} || 工步信息：{step_id}-{step_name} (内置ID: {si_identity}) "
                                    f"工步检测项 [{si_category}:{si_name}] ||  (规则: rule_9999 - 直接复用)")
                                print(f"      🔄 rule_9999: 直接复用历史值 {new_si_value}")
                                
                            elif rule_name in ("rule_1", "rule_2"):
                                rule_type = {"rule_1": "双一致", "rule_2": "数值收敛"}[rule_name]
                                new_si_value = calculator.calculate_process_value(
                                    new_final_value=proc_insp_value,
                                    rule_type=rule_type,
                                    rule_formula=rule_formula
                                )
                                print(f"    [{idx}]  工序信息：{process_id} || 工步信息：{step_id}-{step_name} (内置ID: {si_identity}) "
                                    f"工步检测项 [{si_category}:{si_name}] || (规则: {rule_type})")
                                print(f"      🧮 {rule_type}: 使用公式 '{rule_formula}' 计算得到 {new_si_value}")
                        except Exception as e:
                            print(f"      ❌ 计算失败: {e}")
                            continue

                        if new_si_value is None:
                            continue

                        # 写回 new_graph（同原逻辑，仅保留 4 属性）
                        for step_dict in process_info.get("工步列表", []):
                            if step_dict["工步节点"].get("identity") != step_node.identity:
                                continue
                            found = False
                            for exist_si in step_dict.setdefault("工步检列表", []):
                                if exist_si.get("identity") == si_identity:
                                    exist_si["properties"]["设计值技术指标"] = new_si_value
                                    found = True
                                    break
                            
                            if not found:
                                # 直接取原节点的全部属性，不手动挑选
                                full_props = dict(si_node)   # si_node 就是原工步检节点
                                full_props["设计值技术指标"] = new_si_value   # 仅覆盖需要更新的字段
                                step_dict["工步检列表"].append({
                                    "identity": si_identity,
                                    "labels": list(si_node.labels),
                                    "properties": full_props
                                })
                            total_updated += 1
                            print(f"      ✅ 更新工序{process_id}→工步{step_id} "
                                f"【{si_name}】= {new_si_value} (identity:{si_identity})")
                            break

            print(f"\n✅ 工序检→工步检二次分解完成，共更新 {total_updated} 项工步检")
            return True

        except Exception as e:
            print(f"❌ 步骤5失败：{str(e)}")
            import traceback
            traceback.print_exc()
            return False



    def create3_5_复用标准工步下的资源辅料设备(self):
        """
        create3.5：复用标准工步下的资源辅料设备
        遍历新图中的每个工步，通过工步节点的内置ID查询Neo4j中关联的设备、制造资源和辅料
        并将查询结果添加到对应工步的信息中
        """
        print("\n📋 create3.5：复用标准工步下的资源辅料设备...")
        
        # 前置校验
        if not self.graph:
            print("❌ Neo4j未连接，无法查询资源信息")
            return
            
        if not self.new_graph.get("标准工序工步"):
            print("⚠️ 未找到标准工序工步数据，跳过资源复用")
            return
        
        # 记录处理结果
        total_steps = 0
        total_equipment = 0
        total_resources = 0
        total_materials = 0
        
        # 遍历所有工序
        for process_id, op_info in self.new_graph["标准工序工步"].items():
            # 遍历工序下的所有工步
            for step_info in op_info.get("工步列表", []):
                total_steps += 1
                step_node = step_info.get("工步节点")
                
                if not step_node or "identity" not in step_node:
                    print(f"  ⚠️ 工步节点信息不完整，跳过")
                    continue
                
                step_identity = step_node["identity"]
                step_name = step_node["properties"].get("工步名称", f"工步{step_node['properties'].get('工步号', '')}")
                
                print(f"  处理工步: {step_name} (内置ID: {step_identity})")
                
                # 构建Cypher查询，查找工步关联的设备、制造资源和辅料
                cypher_query = """
                MATCH (step:工步)
                WHERE id(step) = $step_id
                
                // 查询使用的设备
                OPTIONAL MATCH (step)-[:使用设备]->(equipment:设备)
                // 查询使用的制造资源
                OPTIONAL MATCH (step)-[:使用制造资源]->(resource:制造资源)
                // 查询使用的辅料
                OPTIONAL MATCH (step)-[:使用辅料]->(material:辅料)
                
                RETURN 
                    COLLECT(DISTINCT equipment) AS 设备列表,
                    COLLECT(DISTINCT resource) AS 制造资源列表,
                    COLLECT(DISTINCT material) AS 辅料列表
                """
                
                try:
                    # 执行查询
                    result = self.graph.run(cypher_query, step_id=step_identity).data()[0]
                    
                    # 处理查询结果，转换为带ID的字典格式
                    equipment_list = [node_to_dict_with_Id(equip) for equip in result["设备列表"] if equip is not None]
                    resource_list = [node_to_dict_with_Id(res) for res in result["制造资源列表"] if res is not None]
                    material_list = [node_to_dict_with_Id(mat) for mat in result["辅料列表"] if mat is not None]
                    
                    # 将资源信息添加到工步信息中
                    step_info["设备列表"] = equipment_list
                    step_info["制造资源列表"] = resource_list
                    step_info["辅料列表"] = material_list
                    
                    # 更新统计
                    total_equipment += len(equipment_list)
                    total_resources += len(resource_list)
                    total_materials += len(material_list)
                    
                    # 打印详细信息
                    print(f"    关联设备: {len(equipment_list)} 个")
                    print(f"    关联制造资源: {len(resource_list)} 个")
                    print(f"    关联辅料: {len(material_list)} 个")
                    
                except IndexError:
                    print(f"    ⚠️ 未查询到该工步的资源信息")
                except Exception as e:
                    print(f"    ❌ 查询失败: {str(e)}")
        
        # 打印汇总信息
        print(f"\n✅ create3.5 完成：共处理 {total_steps} 个工步")
        print(f"   累计关联设备: {total_equipment} 个")
        print(f"   累计关联制造资源: {total_resources} 个")
        print(f"   累计关联辅料: {total_materials} 个")

    def run(self):
        import sys
        from io import StringIO
        from contextlib import redirect_stdout
        # 如果 debug_save_standard_process_steps 在本文件中，无需额外导入

        stdout_capture = StringIO()
        with redirect_stdout(stdout_capture):
            self.do1_通过产品名称找元件()
            self.do2_获取历史终检节点()
            self.do3_获取工序工步以及对应检测项()
            self.create1_新元件信息()
            self.create2_新终检信息()
            self.create3_复用标准工序工步()
            self.create3_5_复用标准工步下的资源辅料设备()

            # 🔍 调试：调用独立函数保存结构化文件
            standard_steps = self.new_graph.get("标准工序工步")
            debug_dir = Path(self._log_file_path).parent / "debug1_原始数据"
            debug_save_standard_process_steps(standard_steps, str(debug_dir))

            self.create4_终检分解()
            self.create5_工序检再分解()

            # 🔍 调试：调用独立函数保存结构化文件
            standard_steps = self.new_graph.get("标准工序工步")
            debug_dir = Path(self._log_file_path).parent / "debug2_计算之后的数据"
            debug_save_standard_process_steps(standard_steps, str(debug_dir))


        captured_output = stdout_capture.getvalue()
        try:
            if self._log_file_path:
                with open(self._log_file_path, 'w', encoding='utf-8') as f:
                    f.write(captured_output)
        except:
            pass

        sys.stdout.write(captured_output)
        return captured_output
